package com.seele.tigerwallet.ui.orm;

import com.j256.ormlite.dao.RawRowMapper;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.field.types.DateLongType;
import com.j256.ormlite.field.types.DateStringType;

import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class GenericRowMapper<T> implements RawRowMapper<T> {
    private Class<T> entityClass;
    private Set<Field> fields = new HashSet<>();
    private Map<String, Field> colNameFieldMap = new HashMap<>();

    public GenericRowMapper(Class<T> entityClass) {
        this.entityClass = entityClass;
        Class cl = entityClass;

        do {
            for (Field field : cl.getDeclaredFields()) {
                if (field.isAnnotationPresent(DatabaseField.class)) {
                    DatabaseField an = field.getAnnotation(DatabaseField.class);
                    colNameFieldMap.put(an.columnName(), field);
                } else {
                    colNameFieldMap.put(field.getName(), field);
                }
                fields.add(field);
            }
            cl = cl.getSuperclass();
        } while (cl != Object.class);
    }

    @Override
    public T mapRow(String[] columnNames, String[] resultColumns) throws SQLException {
        try {
            T entity = entityClass.newInstance();
            for (int i = 0; i < columnNames.length; i++) {
                Field f = colNameFieldMap.get(columnNames[i]);
                if (null != f) {
                    boolean accessible = f.isAccessible();
                    f.setAccessible(true);
                    f.set(entity, stringToJavaObject(f.getType(), resultColumns[i]));
                    f.setAccessible(accessible);
                }
            }
            return entity;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public Object stringToJavaObject(Class cl, String result) {
        if (result == null) {
            return null;
        } else if (cl == Integer.class || int.class == cl) {
            return Integer.parseInt(result);
        } else if (cl == Long.class || long.class == cl) {
            return Long.parseLong(result);
        } else if (cl == Float.class || float.class == cl) {
            return Float.parseFloat(result);
        } else if (cl == Double.class || double.class == cl) {
            return Double.parseDouble(result);
        } else if (cl == Boolean.class || cl == boolean.class) {
            try {
                return Integer.valueOf(result) > 0;
            } catch (NumberFormatException e) {
                return Boolean.parseBoolean(result);
            }
        } else if (cl == Date.class) {
            DateLongType lType = DateLongType.getSingleton();
            DateStringType sType = DateStringType.getSingleton();
            try {
                return lType.resultStringToJava(null, result, -1);
            } catch (Exception e) {
                try {
                    return sType.resultStringToJava(null, result, -1);
                } catch (SQLException e2) {
                    throw new RuntimeException(e);
                }
            }
        } else {
            return result;
        }
    }
}